package br.com.afi.util.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

/**
 * Essa classe possui diversos métodos utilitários para a session do hibernate.
 * 
 * @author André de Fontana Ignacio
 * @version 1.0
 * @param <E> Tipo da entidade
 * @param <ID> Tipo da chave primária do Model (Entity)
 */
public class SessionManager<E extends Serializable, ID extends Serializable> {
	private Session session;
	private SessionFactory sessionFactory;
	private boolean sessionFactoryMode = false;
	private Class<E> persistentClass;
	
	/**
	 * Construtor
	 */
	@SuppressWarnings("unchecked")
	public SessionManager()
	{
		super();
		this.persistentClass = ( Class<E> ) ( ( ParameterizedType ) getClass().getGenericSuperclass() ).getActualTypeArguments()[0];
	}
	
	/**
	 * Construtor
	 * 
	 * @param session Session
	 */
	@SuppressWarnings("unchecked")
	public SessionManager( Session session )
	{
		super();
		this.persistentClass = ( Class<E> ) ( ( ParameterizedType ) getClass().getGenericSuperclass() ).getActualTypeArguments()[0];
		this.session = session;
	}
	
	/**
	 * Construtor
	 * 
	 * @param sessionFactory Fabríca de Sessões
	 */
	@SuppressWarnings("unchecked")
	public SessionManager( SessionFactory sessionFactory )
	{
		super();
		this.persistentClass = ( Class<E> ) ( ( ParameterizedType ) getClass().getGenericSuperclass() ).getActualTypeArguments()[0];
		this.sessionFactory = sessionFactory;
		this.sessionFactoryMode = true;
	}
	
	/**
	 * Obtém a classe da entidade
	 * 
	 * @return Classe da entidade
	 */
	protected Class<E> getPersistentClass()
	{
		return persistentClass;
	}
	
	/**
	 * Atribui a sessão
	 * 
	 * @param session Session
	 */
	public void setSession( Session session )
	{
		this.session = session;
	}

	/**
	 * Obtém a sessão.
	 * 
	 * @return Session
	 */
	protected Session getSession()
	{
		if ( session == null )
		{
			throw new IllegalStateException( "Sessão precisa ser atribuida antes de ser utilizada." );
		}
		return session;
	}
	
	/**
	 * Obtém a sessão atual. Se a objeto foi construído com um SessionFactory, obtém a sessão através do
	 * {@link SessionFactory#getCurrentSession()}, caso contrário obtém a sessão atribuída ao objeto.
	 * 
	 * @return Session
	 */
	protected final Session getCurrentSession()
	{
		Session currentSession = null;
		if( sessionFactoryMode )
		{
			currentSession = sessionFactory.getCurrentSession();
		}
		else
		{
			currentSession = getSession();
		}
		return currentSession;
	}
	
	/**
	 * Busca a entidade através da sua chave primária.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(Serializable, boolean)}
	 * 
	 * @param id Chave primária
	 * @param lock Se verdadeiro, trava a tabela para execução de updates.
	 * @return Entity
	 */
	@SuppressWarnings("unchecked")
	public E findById( ID id, boolean lock )
	{
		E entity = null;
		if ( lock )
			entity = ( E ) getCurrentSession().load( getPersistentClass(), id, LockMode.UPGRADE );
		else
			entity = ( E ) getCurrentSession().load( getPersistentClass(), id );

		return entity;
	}
	
	/**
	 * Obtém a entidade através da sua chave primária.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findById(Serializable, boolean)}
	 * 
	 * @param id Chave primária
	 * @param lock Se verdadeiro, trava a tabela para execução de updates.
	 * @return Entity
	 */
	public E get( ID id, boolean lock )
	{
		return findById(id, lock);
	}
	
	/**
	 * Busca a entidade através da sua chave primária.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(Serializable)}
	 * 
	 * @param id Chave primária
	 * @return Entity
	 */
	@SuppressWarnings("unchecked")
	public E findById( ID id )
	{
		return ( E ) getCurrentSession().load( getPersistentClass(), id );
	}
	
	/**
	 * Obtém a entidade através da sua chave primária.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findById(Serializable)}
	 * 
	 * @param id Chave primária
	 * @return Entity
	 */
	public E get( ID id )
	{
		return findById( id );
	}

	/**
	 * Verifica se alguma entidade possui o Id informado.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #contains(Serializable)}
	 * 
	 * @param id Id
	 * @return Verdadeiro se existir
	 */
	public boolean existsById( ID id )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );
		crit.add( Restrictions.idEq( id ) );

		return ( ( Number ) crit.uniqueResult() ).longValue() != 0L;
	}
	
	/**
	 * Verifica se alguma entidade possui o Id informado.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #existsById(Serializable)}
	 * 
	 * @param id Id
	 * @return Verdadeiro se existir
	 */
	public boolean contains( ID id )
	{
		return existsById( id );
	}
	
	/**
	 * Verifica se existe algum registro no banco que possui com os critérios informados.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param criterion Critérios de Busca
	 * @return Verdadeiro se existir um registro no banco com os critérios informados
	 */
	protected boolean existsByCriteria( Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );

		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		return ( ( Number ) crit.uniqueResult() ).longValue() != 0L;
	}

	/**
	 * Este método retorna a quantidade de registros buscando pelos critérios informados.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param criterion Critérios de busca
	 * @return Quantidade de registros
	 */
	protected Number countByCriteria( Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		return ( ( Number ) crit.uniqueResult() );
	}

	/**
	 * Obtém a quantidade de entities.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #count()}
	 * 
	 * @return Quantidade de entities
	 */
	public Number countAll()
	{
		return this.countByCriteria();
	}
	
	/**
	 * Obtém a quantidade de entities.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #countAll()}
	 * 
	 * @return Quantidade de entities
	 */
	public Number count()
	{
		return countAll();
	}

	/**
	 * Busca todos as entidades.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get()}
	 * 
	 * @return Lista de entidades
	 */
	public List<E> findAll()
	{
		return findByCriteria();
	}
	
	/**
	 * Obtém todas as entidades.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findAll()}
	 * 
	 * @return Lista de entidades
	 */
	public List<E> get()
	{
		return findAll();
	}

	/**
	 * Busca todas as entidades utilizando paginação.
	 * <br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(int, int)}
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @return Lista de entidades
	 */
	public List<E> findAll( int firstResult, int maxResults )
	{
		return findByCriteria( firstResult, maxResults );
	}
	
	/**
	 * Obtém todas as entidades utilizando paginação.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findAll(int, int)}
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @return Lista de entidades
	 */
	public List<E> get( int firstResult, int maxResults )
	{
		return findAll( firstResult, maxResults );
	}

	/**
	 * Busca somente alguns atributos de todas as entidades.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(Projection)}
	 * 
	 * @param projection Projeções
	 * @return Lista de Objetos
	 */
	@SuppressWarnings("unchecked")
	protected List<Object> findAll( Projection projection )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setProjection( projection );
		return crit.list();
	}
	
	/**
	 * Obtém somente alguns atributos de todas de entidades.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findAll(Projection)}
	 * 
	 * @param projection Projeções
	 * @return Lista de Objetos
	 */
	protected List<Object> get( Projection projection )
	{
		return findAll( projection );
	}

	/**
	 * Busca as entidades ordenando pela paramêtro especificado utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(int, int, Order...)}
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param order Ordens de Busca
	 * @return Lista de entidades
	 */
	protected List<E> findAllOrderBy( int firstResult, int maxResults, Order... order )
	{
		return this.findByCriteriaOrderBy( firstResult, maxResults, null, order );
	}
	
	/**
	 * Obtém as entidades ordenando pela paramêtro especificado utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findAllOrderBy(int, int, Order...)}
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param order Ordens de Busca
	 * @return Lista de entidades
	 */
	protected List<E> get( int firstResult, int maxResults, Order... order )
	{
		return findAllOrderBy(firstResult, maxResults, order);
	}

	/**
	 * Busca as entidades ordenando pela paramêtro especificado e utilizando
	 * paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #get(Order...)}
	 * 
	 * @param order Ordens de Busca
	 * @return Lista de entidades
	 */
	protected List<E> findAllOrderBy( Order... order )
	{
		return findByCriteriaOrderBy( null, order );
	}
	
	/**
	 * Obtém as entidades ordenando pela paramêtro especificado e utilizando
	 * paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.<br>
	 * Esse método possui a mesma funcionalidade do método {@link #findAllOrderBy(Order...)}
	 * 
	 * @param order Ordens de Busca
	 * @return Lista de entidades
	 */
	protected List<E> get( Order... order )
	{
		return findAllOrderBy( order );
	}

	/**
	 * Busca por um entidade através de uma instância exemplo.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param excludeProperty Propriedades a serem desconsideradas durante na busca
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	public List<E> findByExample( E exampleInstance, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.add( example );
		return crit.list();
	}

	/**
	 * Filtra uma coleção através de um HQL. Utilize no método conveniente nas subclasses.
	 * 
	 * @param collection Coleção
	 * @param hqlFilter Query de filtro em HQL(Hibernate Query Language)
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> filterWithHQL( List<? extends Serializable> collection, String hqlFilter )
	{
		final Query q = getCurrentSession().createFilter( collection, hqlFilter );
		return q.list();
	}

	/**
	 * Filtra uma coleção através de um HQL utilizando paginação. Utilize no método conveniente nas
	 * subclasses.
	 * 
	 * @param collection Coleção
	 * @param hqlFilter Query de filtro em HQL(Hibernate Query Language)
	 * @param firstResult Número do primeiro registro
	 * @param maxResults Número maximo de registros
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> filterWithHQL( List<? extends Serializable> collection, String hqlFilter, int firstResult, int maxResults )
	{
		final Query q = getCurrentSession().createFilter( collection, hqlFilter );
		q.setFirstResult( firstResult );
		q.setMaxResults( maxResults );
		return q.list();
	}

	public List<E> findByExampleILike( E exampleInstance, String... excludeProperty )
	{
		return this.findByExampleILike( exampleInstance, false, excludeProperty );
	}

	public List<E> findByExampleILike( E exampleInstance, boolean includeRelationships, String... excludeProperty )
	{
		return this.findByExampleILikeCriteria( exampleInstance, includeRelationships, null, excludeProperty );
	}

	/**
	 * Busca através do exemplo informado utilizando insensitive like e pelos critérios ordenando
	 * pelo parâmetro específicado. Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param criterions Critérios
	 * @param orders Ordenações
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByExampleILikeCriteriaOrderBy( E exampleInstance, boolean includeRelationships, Criterion[] criterions, Order[] orders, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();

		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance, excludeProperty );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		if ( criterions != null )
		{
			for ( Criterion criterion : criterions )
			{
				crit.add( criterion );
			}
		}
		if ( orders != null )
		{
			for ( Order order : orders )
			{
				crit.addOrder( order );
			}
		}
		return crit.list();
	}

	/**
	 * Busca através do exemplo informado com projeção utilizando insensitive like e pelos critérios
	 * informados. <br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param projection Projeção
	 * @param criterions Critérios
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	protected List<E> findByExampleILikeCriteria( E exampleInstance, boolean includeRelationships, Projection projection, Criterion[] criterions, String... excludeProperty )
	{
		return this.findByExampleILikeCriteriaOrderBy( exampleInstance, includeRelationships, criterions, null, excludeProperty );
	}

	/**
	 * Busca através do exemplo informado com projeção utilizando insensitive like e pelos critérios
	 * ordenando pelo paramêtro específicado. <br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param projection Projeção
	 * @param criterions Critérios
	 * @param orders Ordenações
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByExampleILikeCriteriaOrderBy( E exampleInstance, boolean includeRelationships, Projection projection, Criterion[] criterions, Order[] orders, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		if ( criterions != null )
		{
			for ( Criterion criterion : criterions )
			{
				crit.add( criterion );
			}
		}
		if ( orders != null )
		{
			for ( Order order : orders )
			{
				crit.addOrder( order );
			}
		}
		crit.setProjection( projection );
		return crit.list();
	}

	/**
	 * Busca através do exemplo informado utilizando like e ordenando pelo paramêtro específicado.
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param orders Ordenações
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByExampleILikeOrderBy( E exampleInstance, boolean includeRelationships, Order[] orders, int firstResult, int maxResults, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		crit.setFirstResult( firstResult );
		crit.setMaxResults( maxResults );
		if ( orders != null )
		{
			for ( Order order : orders )
			{
				crit.addOrder( order );
			}
		}

		return crit.list();
	}

	/**
	 * Busca através do exemplo informado utilizando insensitive like e pelos critérios. Utilize no
	 * método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param criterions Critérios
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	protected List<E> findByExampleILikeCriteria( E exampleInstance, boolean includeRelationships, Criterion[] criterions, String... excludeProperty )
	{
		return this.findByExampleILikeCriteriaOrderBy( exampleInstance, includeRelationships, criterions, null, excludeProperty );
	}

	@SuppressWarnings("unchecked")
	public List<E> findByExampleILike( E exampleInstance, boolean includeRelationships, int firstResult, int maxResults, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		crit.setFirstResult( firstResult );
		crit.setMaxResults( maxResults );
		return crit.list();
	}

	/**
	 * Busca através do exemplo informado utilizando like e ordenando pelo paramêtro específicado.
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param criterions Critérios
	 * @param orders Ordenações
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByExampleILikeCriteriaOrderBy( E exampleInstance, boolean includeRelationships, Criterion[] criterions, Order[] orders, int firstResult, int maxResults, String... excludeProperty )
	{
		return ( List<E> ) this.findByExampleILikeCriteriaOrderBy( exampleInstance, includeRelationships, null, criterions, orders, firstResult, maxResults, excludeProperty );
	}

	/**
	 * Busca através do exemplo informado com projeção utilizando like e ordenando pelo paramêtro
	 * específicado.<br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param projection Projeção
	 * @param criterions Critérios
	 * @param orders Ordenações
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByExampleILikeCriteriaOrderBy( E exampleInstance, boolean includeRelationships, Projection projection, Criterion[] criterions, Order[] orders, int firstResult, int maxResults, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		for ( Criterion criterion : criterions )
		{
			crit.add( criterion );
		}
		crit.add( example );
		crit.setFirstResult( firstResult );
		crit.setMaxResults( maxResults );
		if ( orders != null )
		{
			for ( Order order : orders )
			{
				crit.addOrder( order );
			}
		}
		// if ( projection != null )
		// {
		crit.setProjection( projection );
		// }
		return crit.list();
	}

	public Number countByExample( E exampleInstance, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );
		crit.add( example );
		return ( Integer ) crit.uniqueResult();
	}

	public Number countByExampleILike( E exampleInstance, boolean includeRelationships, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		return ( Integer ) crit.uniqueResult();
	}

	/**
	 * Obtém a quantidade de registros através do exemplo informado utilizando insensitive like.<br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Instância de exemplo
	 * @param includeRelationships Incluir relacionamentos?
	 * @param criterions Critérios
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Quantidade de Entidades
	 */
	protected Number countByExampleILikeCriteria( E exampleInstance, boolean includeRelationships, Criterion[] criterions, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );
		crit.setProjection( Projections.rowCount() );
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		for ( Criterion criterion : criterions )
		{
			crit.add( criterion );
		}

		crit.add( example );
		return ( ( Number ) crit.uniqueResult() );
	}

	/**
	 * Obtém a quantidade de registros através do exemplo informado (com insensitive like) e pelos
	 * critérios informados, realizando distinct através da propriedade informada.<br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Entidade de exemplo
	 * @param distinctProperty Propriedade pela qual será realizado o distinct
	 * @param includeRelationships Incluir relacionamentos?
	 * @param criterions Critérios
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Quantidade de registros
	 */
	protected Number countDistinctByExampleILikeCriteria( E exampleInstance, String distinctProperty, boolean includeRelationships, Criterion[] criterions, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.setProjection( Projections.countDistinct( distinctProperty ) );
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		for ( Criterion criterion : criterions )
		{
			crit.add( criterion );
		}
		crit.add( example );
		return ( ( Number ) crit.uniqueResult() );
	}

	/**
	 * Obtém a quantidade de registros através do exemplo informado (com insensitive like),
	 * realizando distinct através da propriedade informada.<br>
	 * Utilize no método conveniente nas subclasses.
	 * 
	 * @param exampleInstance Entidade de exemplo
	 * @param distinctProperty Propriedade pela qual será realizado o distinct
	 * @param includeRelationships Incluir relacionamentos?
	 * @param excludeProperty Propriedades a serem desconsideradas na busca
	 * @return Quantidade de registros
	 */
	protected Number countDistinctByExampleILike( E exampleInstance, String distinctProperty, boolean includeRelationships, String... excludeProperty )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		final Example example = Example.create( exampleInstance );
		example.enableLike();
		example.ignoreCase();
		for ( String exclude : excludeProperty )
		{
			example.excludeProperty( exclude );
		}
		crit.setProjection( Projections.countDistinct( distinctProperty ) );
		if ( includeRelationships )
		{
			final List<Criterion> criterios = CriteriaUtil.buildRelationshipsCriterions( exampleInstance );
			for ( Criterion criterion : criterios )
			{
				crit.add( criterion );
			}
		}
		crit.add( example );
		return ( ( Number ) crit.uniqueResult() );
	}

	/**
	 * Altera o estado da entidade para persistente.
	 * 
	 * @param entity Entidade
	 * @return Entidade
	 */
	public E makePersistent( E entity )
	{
		final Session session = getCurrentSession();
		session.saveOrUpdate( entity );
		session.flush();
		return entity;
	}

	/**
	 * Altera o estado da entidade para transiente.
	 * 
	 * @param entity Entidade
	 */
	public void makeTransient( E entity )
	{
		final Session session = getCurrentSession();
		session.delete( entity );
		session.flush();
	}
	
	/**
	 * Adiciona a entidade.
	 * 
	 * @param entity Entidade
	 * @return Entidade
	 */
	public E add( E entity )
	{
		final Session session = getCurrentSession();
		session.persist( entity );
		session.flush();
		return entity;
	}
	
	/**
	 * Se houver uma entidade persistida com o mesmo identificador, obtém a entidade e altera seu estado para persistido, caso
	 * contrário apenas altera o estado da entidade para persistido.
	 * 
	 * @param entity Entidade
	 * @return Entidade
	 */
	public E merge( E entity )
	{
		final Session session = getCurrentSession();
		session.merge( entity );
		session.flush();
		return entity;
	}

	/**
	 * Remove a entidade.
	 * 
	 * @param entity Entidade
	 * @return Entidade
	 */
	public void remove( E entity )
	{
		final Session session = getCurrentSession();
		session.delete( entity );
		session.flush();
	}

	/**
	 * Efetiva o envio das informações.
	 */
	public void flush()
	{
		getCurrentSession().flush();
	}

	/**
	 * Limpa o buffer.
	 */
	public void clear()
	{
		getCurrentSession().clear();
	}

	/**
	 * Busca pelos critérios informados.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param criterion Critérios de busca
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByCriteria( Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		return crit.list();
	}

	/**
	 * Busca pelos critérios informados, utilizando a projeção informada.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param projection Projeção
	 * @param criterion Critérios
	 * @return Lista de objetos
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByCriteria( Projection projection, Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setProjection( projection );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		return crit.list();
	}

	/**
	 * Busca pelos critérios informados, utilizando a projeção informada.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param distinctProperty Propriedade pela qual será realizado o distinct
	 * @param criterion Critérios
	 * @return Quantidade de registros
	 */
	protected Number countDistinctByCriteria( String distinctProperty, Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setProjection( Projections.countDistinct( distinctProperty ) );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		return ( Number ) crit.uniqueResult();
	}

	/**
	 * Busca pelos critérios informados, utilizando a projeção informada.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param projection Projeção
	 * @param criterion Critérios
	 * @param order Ordenações
	 * @return Lista de objetos
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByCriteriaOrderBy( Projection projection, Criterion criterion[], Order... order )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		crit.setProjection( projection );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		for ( Order o : order )
		{
			crit.addOrder( o );
		}
		return crit.list();
	}

	/**
	 * Busca pelos critérios informados travando ou não a entitidade.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param lock Travar a entidade?
	 * @param criterion Critérios de busca
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByCriteria( boolean lock, Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		crit.setLockMode( LockMode.WRITE );
		return crit.list();
	}

	/**
	 * Busca pelos critérios informados utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param criterion Critérios de busca
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByCriteria( int firstResult, int maxResults, Criterion... criterion )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		for ( Criterion c : criterion )
		{
			crit.add( c );
		}
		crit.setFirstResult( firstResult );
		crit.setMaxResults( maxResults );
		return crit.list();
	}

	/**
	 * Busca pelos critérios informados ordenando pelos paramêtros informados.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param criterion Critérios de busca
	 * @param order Ordenações
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByCriteriaOrderBy( Criterion criterion[], Order... order )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		if ( criterion != null )
		{
			for ( Criterion c : criterion )
			{
				crit.add( c );
			}
		}
		for ( Order o : order )
		{
			crit.addOrder( o );
		}

		return crit.list();
	}

	/**
	 * Busca pelos critérios informados utilizando paginação ordenando pelos paramêtros informados.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param firstResult Número do primeiro registro.
	 * @param maxResults Número maximo de registros.
	 * @param order Ordenações
	 * @param criterion Critérios de busca
	 * @return Lista de Entitys
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByCriteriaOrderBy( int firstResult, int maxResults, Criterion[] criterion, Order... order )
	{
		final Criteria crit = getCurrentSession().createCriteria( getPersistentClass() );
		if ( criterion != null )
		{
			for ( Criterion c : criterion )
			{
				crit.add( c );
			}
		}
		for ( Order o : order )
		{
			crit.addOrder( o );
		}
		crit.setFirstResult( firstResult );
		crit.setMaxResults( maxResults );
		return crit.list();
	}

	/**
	 * Busca através de uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByHQL( String query )
	{
		final Query q = getCurrentSession().createQuery( query );
		return q.list();
	}

	/**
	 * Executa uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @return Quantidade de entidades alteradas ou removidas
	 */
	protected int executeHQL( String query )
	{
		return this.executeHQL( query, null );
	}

	/**
	 * Executa uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @param parameters Paramêtros da query
	 * @return Quantidade de entidades alteradas ou removidas
	 */
	protected int executeHQL( String query, Map<String, Object> parameters )
	{
		final Query q = getCurrentSession().createQuery( query );
		QueryUtil.fillParameters( q, parameters );
		return q.executeUpdate();
	}

	/**
	 * Remove os registros através das restrições em HQL(Hibernate Query Language) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @return Quantidade de entidades removidas
	 */
	protected int deleteByHQLRestriction( String entityAlias, String hqlRestriction )
	{
		return this.deleteByHQLRestriction( entityAlias, hqlRestriction, null );
	}

	/**
	 * Remove os registros através das restrições em HQL(Hibernate Query Language) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param parameters Paramêtros da query
	 * @return Quantidade de entidades removidas
	 */
	protected int deleteByHQLRestriction( String entityAlias, String hqlRestriction, Map<String, Object> parameters )
	{
		final StringBuilder sb = new StringBuilder();
		sb.append( "delete from " );
		sb.append( getPersistentClass().getSimpleName() );
		sb.append( " " );
		sb.append( entityAlias );

		if ( hqlRestriction != null && !"".equals( hqlRestriction ) )
		{
			sb.append( " where " );
			sb.append( hqlRestriction );
		}

		final Query q = getCurrentSession().createQuery( sb.toString() );
		QueryUtil.fillParameters( q, parameters );
		return q.executeUpdate();
	}

	/**
	 * Busca através de uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @return Quantidade de entidades
	 */
	protected Number countByHQL( String query )
	{
		final Query q = getCurrentSession().createQuery( query );
		return ( ( Number ) q.uniqueResult() ).intValue();
	}

	/**
	 * Obtém a quantidade de registros através de uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @param parameters Paramêtros da query
	 * @return Lista de entidades
	 */
	protected Number countByHQL( String query, Map<String, Object> parameters )
	{
		final Query q = getCurrentSession().createQuery( query );
		QueryUtil.fillParameters( q, parameters );
		return ( ( Number ) q.uniqueResult() ).intValue();
	}

	/**
	 * Busca através de uma condição (where) em HQL (Hibernate Query Language). <br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @return Lista de entidades
	 */
	protected List<E> findByHQLRestriction( String entityAlias, String hqlRestriction )
	{
		return this.findByHQLRestriction( entityAlias, hqlRestriction, null );
	}

	/**
	 * Busca através de uma condição (where) em HQL (Hibernate Query Language). <br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param parameters Paramêtros da query
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByHQLRestriction( String entityAlias, String hqlRestriction, Map<String, Object> parameters )
	{
		final StringBuilder sb = new StringBuilder();
		sb.append( "Select " );
		sb.append( entityAlias );
		sb.append( " " );
		sb.append( " from " );
		sb.append( getPersistentClass().getSimpleName() );
		sb.append( " " );
		sb.append( entityAlias );

		if ( hqlRestriction != null && !"".equals( hqlRestriction ) )
		{
			sb.append( " where " );
			sb.append( hqlRestriction );
		}

		final Query q = getCurrentSession().createQuery( sb.toString() );
		QueryUtil.fillParameters( q, parameters );
		return q.list();
	}

	/**
	 * Verifica se existe algum registro no banco que possui as condições (where) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @return Verdadeiro se existir
	 */
	protected boolean existsByHQLRestriction( String entityAlias, String hqlRestriction )
	{
		return this.existsByHQLRestriction( entityAlias, hqlRestriction, null );
	}

	/**
	 * Verifica se existe algum registro no banco que possui as condições (where) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param parameters Paramêtros da query
	 * @return Verdadeiro se existir
	 */
	protected boolean existsByHQLRestriction( String entityAlias, String hqlRestriction, Map<String, Object> parameters )
	{
		final StringBuilder sb = new StringBuilder();
		sb.append( "Select count(*) from " );
		sb.append( getPersistentClass().getSimpleName() );
		sb.append( " " );
		sb.append( entityAlias );

		if ( hqlRestriction != null && !"".equals( hqlRestriction ) )
		{
			sb.append( " where " );
			sb.append( hqlRestriction );
		}

		final Query q = getCurrentSession().createQuery( sb.toString() );
		QueryUtil.fillParameters( q, parameters );
		final Number result = ( Number ) q.uniqueResult();
		return result.longValue() != 0L;
	}

	/**
	 * Este método retorna a quantidade de registros buscando pelas condições (where) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições após o where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @return Quantidade de registros
	 */
	protected Number countByHQLRestriction( String entityAlias, String hqlRestriction )
	{
		return this.countByHQLRestriction( entityAlias, hqlRestriction, null );
	}

	/**
	 * Este método retorna a quantidade de registros buscando pelas condições (where) informadas.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param parameters Paramêtros da query
	 * @return Quantidade de registros
	 */
	protected Number countByHQLRestriction( String entityAlias, String hqlRestriction, Map<String, Object> parameters )
	{
		final StringBuilder sb = new StringBuilder();
		sb.append( "Select count(*) from " );
		sb.append( getPersistentClass().getSimpleName() );
		sb.append( " " );
		sb.append( entityAlias );

		if ( hqlRestriction != null && !"".equals( hqlRestriction ) )
		{
			sb.append( " where " );
			sb.append( hqlRestriction );
		}

		final Query q = getCurrentSession().createQuery( sb.toString() );
		QueryUtil.fillParameters( q, parameters );
		return ( ( Number ) q.uniqueResult() ).intValue();
	}

	/**
	 * Busca através de uma Query em HQL (Hibernate Query Language) utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param hqlRestriction Query em HQL(Hibernate Query Language)
	 * @param firstResult Primeiro elemento
	 * @param maxResults Quantidade máxima de elementos
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByHQL( String hqlRestriction, int firstResult, int maxResults )
	{
		final Query q = getCurrentSession().createQuery( hqlRestriction );
		q.setFirstResult( firstResult );
		q.setMaxResults( maxResults );
		return q.list();
	}

	/**
	 * Busca através de uma condição (where) em HQL (Hibernate Query Language) utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições após o where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param firstResult Primeiro elemento
	 * @param maxResults Quantidade máxima de elementos
	 * @return Lista de entidades
	 */
	protected List<E> findByHQLRestriction( String entityAlias, String hqlRestriction, int firstResult, int maxResults )
	{
		return this.findByHQLRestriction( entityAlias, hqlRestriction, null, firstResult, maxResults );
	}

	/**
	 * Busca através de uma condição (where) em HQL (Hibernate Query Language) utilizando paginação.<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param entityAlias Apelido que será usado para a Entidade em questão
	 * @param hqlRestriction Somente as condições do where de uma query em HQL(Hibernate Query
	 *            Language)
	 * @param parameters Paramêtros da query
	 * @param firstResult Primeiro elemento
	 * @param maxResults Quantidade máxima de elementos
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByHQLRestriction( String entityAlias, String hqlRestriction, Map<String, Object> parameters, int firstResult, int maxResults )
	{
		final StringBuilder sb = new StringBuilder();
		sb.append( "Select " );
		sb.append( entityAlias );
		sb.append( " " );
		sb.append( " from " );
		sb.append( getPersistentClass().getSimpleName() );
		sb.append( " " );
		sb.append( entityAlias );

		if ( hqlRestriction != null && !"".equals( hqlRestriction ) )
		{
			sb.append( " where " );
			sb.append( hqlRestriction );
		}
		final Query q = getCurrentSession().createQuery( sb.toString() );
		QueryUtil.fillParameters( q, parameters );
		q.setFirstResult( firstResult );
		q.setMaxResults( maxResults );
		return q.list();
	}

	/**
	 * Busca através de uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @param parameters Paramêtros da query
	 * @param firstResult Primeiro elemento
	 * @param maxResults Quantidade máxima de elementos
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByHQL( String query, Map<String, Object> parameters, int firstResult, int maxResults )
	{
		final Query q = getCurrentSession().createQuery( query );
		QueryUtil.fillParameters( q, parameters );
		q.setFirstResult( firstResult );
		q.setMaxResults( maxResults );
		return q.list();
	}

	/**
	 * Busca através de uma Query em HQL (Hibernate Query Language).<br>
	 * Use este método dentro das subclasses no método conveniente.
	 * 
	 * @param query Query em HQL(Hibernate Query Language)
	 * @param parameters Paramêtros da query
	 * @return Lista de entidades
	 */
	@SuppressWarnings("unchecked")
	protected List<? extends Serializable> findByHQL( String query, Map<String, Object> parameters )
	{
		final Query q = getCurrentSession().createQuery( query );
		QueryUtil.fillParameters( q, parameters );
		return q.list();
	}

	/**
	 * Realiza a paginação na lista.
	 * 
	 * @param list Lista onde será feita a paginação
	 * @param firstResult Primeiro elemento
	 * @param maxResults Quantidade máxima de elementos
	 * @return Sub-lista gerada
	 */
	public List<E> doPagination( List<E> list, int firstResult, int maxResults )
	{
		int toIndex = firstResult + maxResults;
		if ( toIndex > list.size() )
		{
			toIndex = list.size();
		}

		return list.subList( firstResult, toIndex );
	}
	
	/**
	 * Obtém o primeiro objeto da lista, se não existir retorna nulo.
	 * 
	 * @param list Lista
	 * @return Primeiro objeto da lista, se não existir retorna nulo.
	 */
	public E uniqueResult( List<E> list )
	{
		return list.isEmpty() ? null : list.get( 0 );
	}

	/**
	 * Obtém o primeiro objeto da lista, se não existir retorna nulo.
	 * 
	 * @param list Lista
	 * @return Primeiro objeto da lista, se não existir retorna nulo.
	 */
	public Object uniqueResultObject( List<? extends Object> list )
	{
		return list.isEmpty() ? null : list.get( 0 );
	}
}
